#!/usr/bin/env python
# coding: utf-8

# # 投资组合 Portfolio

# ## 准备代码 Prepare 

# In[1]:


import numpy as np # 数据处理最重要的模块
import pandas as pd # 数据处理最重要的模块

#统计与回归库
import scipy.stats as stats # 统计模块
import scipy
# import pymysql  # 导入数据库模块

from datetime import datetime # 时间模块
import statsmodels.formula.api as smf  # OLS regression

# import pyreadr # read RDS file


#可视化库
from matplotlib import style
import matplotlib.pyplot as plt  # 画图模块
import matplotlib.dates as mdates

from matplotlib.font_manager import FontProperties # 作图中文
from pylab import mpl
#mpl.rcParams['font.sans-serif'] = ['SimHei']
#plt.rcParams['font.family'] = 'Times New Roman'

#输出矢量图 渲染矢量图
get_ipython().run_line_magic('matplotlib', 'inline')
get_ipython().run_line_magic('config', "InlineBackend.figure_format = 'svg'")

from IPython.core.interactiveshell import InteractiveShell # jupyter运行输出的模块
#显示每一个运行结果
InteractiveShell.ast_node_interactivity = 'all'

#设置行不限制数量
#pd.set_option('display.max_rows',None)

#设置列不限制数量
pd.set_option('display.max_columns', None)


# ## 投资组合的构造方法

# ## 个股月度数据 Monthly Data of Individual Stocks

# - Stkcd [证券代码] - 以上交所、深交所公布的证券代码为准
# - Trdmnt [交易月份] - 以YYYY-MM表示
# - Opndt [月开盘日期] - 月第一个交易日。以DD表示，为OPNPRC的所在日，“DD”＝本月无交易
# - Mopnprc [月开盘价] - 月第一个交易日的开盘价。
# - Clsdt [月收盘日期] - 月最后一个交易日。以DD表示，为CLSPRC的所在日，“DD”＝本月无交易
# - Mclsprc [月收盘价] - 月最后一个交易日的收盘价。
# - Mnshrtrd [月个股交易股数] - 月内该股票的交易数量。计算公式为：月开盘日期与交易日期期间内日成交量之和。
# - Mnvaltrd [月个股交易金额] - 月内该股票的交易金额。计算公式为：月开盘日期与交易日期期间内日成交额之和。A股以人民币元计，上海B以美元计，深圳B以港币计。
# - Msmvosd [月个股流通市值] - 个股的流通股数与月收盘价的乘积。计算公式为：个股的流通股数与月收盘价的乘积。 A股以人民币元计，上海B以美元计，深圳B以港币计，注意单位是千
# - Msmvttl [月个股总市值] - 个股的发行总股数与月收盘价的乘积。计算公式为：个股的发行总股数与月收盘价的乘积，A股以人民币元计，上海B股以美元计，深圳B股以港币计，注意单位是千
# - Ndaytrd [月交易天数] - 计算公式为：月内实际交易的天数之和。
# - Mretwd [考虑现金红利再投资的月个股回报率] - 字段说明见说明书“周、月、年个股回报率的计算方法”。
# - Mretnd [不考虑现金红利再投资的月个股回报率] - 字段说明见说明书“周、月、年个股回报率的计算方法”。
# - Markettype [市场类型] - 1=上证A股市场 (不包含科创板），2=上证B股市场，4=深证A股市场（不包含创业板），8=深证B股市场，16=创业板， 32=科创板，64=北证A股市场。
# - Capchgdt [最新股本变动日期] - 上市公司最近一次股本发生变化的日期
# - Ahshrtrd_M [月盘后成交总量] - 月科创板股票盘后总成交量。计算公式为：月开盘日期与交易日期期间内日盘后成交量之和。
# - Ahvaltrd_M [月盘后成交总额] - 月科创板股票盘后总成交额。计算公式为：月开盘日期与交易日期期间内日盘后成交额之和。

# In[2]:


cross = pd.read_csv('D:/笃行楼209/TRD_Mnth202509.csv')
from pandas.tseries.offsets import MonthEnd
# 处理月份格式
cross['month'] = pd.to_datetime(cross['Trdmnt'], format='%Y-%m') + MonthEnd(1)

#对股票代码 Stkcd 列进行处理，如果股票代码不足6位，就在前面补0，生成标准的6位股票代码
#这是因为在金融数据中，股票代码通常有固定的位数格式，补0后能保证代码格式的一致性，方便后续数据匹配。

cross['Stkcd'] = cross['Stkcd'].apply(lambda x: '{:0>6}'.format(x)) # 6位股票代码
# 重命名列以匹配后续代码
cross.rename(columns={'Mretwd': 'Return', 'Msmvosd': 'floatingvalue', 'Msmvttl': 'totalvalue'}, inplace=True)

# 导入无风险利率数据
rf_data = pd.read_csv('D:/笃行楼209/Marketret_mon_stock2024.csv')
# 处理月份格式
rf_data['month'] = pd.to_datetime(rf_data['month'], format='%b %Y') + MonthEnd(1)
# 只保留需要的列
rf_data = rf_data[['month', 'rfmonth']]

# 合并无风险利率到cross
cross = pd.merge(cross, rf_data, on='month', how='left')

# 计算超额收益率,流通市值和总市值的值扩大1000倍
cross['ret'] = cross['Return'] - cross['rfmonth']
cross['floatingvalue'] = cross['floatingvalue'] * 1000
cross['totalvalue'] = cross['totalvalue'] * 1000
cross


# In[3]:


# 生成下一个月的收益率

# 方法1：补全所有个股的日期序列，再使用shift
# 创建所有日期和股票代码的完整组合
all_months = pd.DataFrame(cross['month'].unique(), columns=['month'])
all_stocks = pd.DataFrame(cross['Stkcd'].unique(), columns=['Stkcd'])
# 创建笛卡尔积 - 所有股票和所有月份的组合
full_index = all_stocks.merge(all_months, how='cross')

# 将原始数据合并到完整索引中
cross_full = full_index.merge(cross, on=['Stkcd', 'month'], how='left')
# 按股票代码和月份排序
cross_full = cross_full.sort_values(['Stkcd', 'month'])
# 使用shift生成下月收益率
cross_full['next_ret_method1'] = cross_full.groupby('Stkcd')['ret'].shift(-1)

# 只保留原始数据存在的行
cross = cross.merge(cross_full[['Stkcd', 'month', 'next_ret_method1']], 
                    on=['Stkcd', 'month'], how='left')

print("方法1完成：补全日期序列后使用shift")
cross[['Stkcd', 'month', 'ret', 'next_ret_method1']].head(20)


# In[4]:


# 方法2：截取个股收益率数据，日期加一个月，重命名后合并回去
# 创建下月收益率数据
next_ret_data = cross[['Stkcd', 'month', 'ret']].copy()
# 月份减少一个月
next_ret_data['month'] = next_ret_data['month'] - MonthEnd(1)
# 重命名收益率列
next_ret_data.rename(columns={'ret': 'next_ret_method2'}, inplace=True)

# 合并回原数据
cross = cross.merge(next_ret_data, on=['Stkcd', 'month'], how='left')

print("方法2完成：日期加一个月后合并")
cross[['Stkcd', 'month', 'ret', 'next_ret_method1', 'next_ret_method2']].head(20)


# In[5]:


# 验证两种方法的结果是否一致
comparison = cross[['Stkcd', 'month', 'ret', 'next_ret_method1', 'next_ret_method2']].copy()
comparison['difference'] = comparison['next_ret_method1'] - comparison['next_ret_method2']

print("两种方法的差异统计：")
print(comparison['difference'].describe())
print(f"\n完全一致的记录数: {(comparison['difference'].abs() < 1e-10).sum()}")
print(f"总记录数: {len(comparison)}")

# 使用方法2的结果作为最终的next_ret
cross['next_ret'] = cross['next_ret_method2']
# 删除临时列
cross.drop(['next_ret_method1', 'next_ret_method2'], axis=1, inplace=True)

cross


# ## 策略细节

# In[6]:


#计算超额收益的月度分位数
#过滤超额收益为空的记录，按月份分组，计算分位数

fenweishu = pd.DataFrame(
    cross[cross['ret'].notna()].groupby(['month'])['ret'].quantile([0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]))
fenweishu


# In[7]:


fenweishu = fenweishu.reset_index()
fenweishu


# In[8]:


#格式重塑

fenweishu = fenweishu.pivot_table(index='month',columns='level_1',values='ret')
fenweishu


# In[9]:


#分位数数据表的列名重命名

fenweishu.columns = ['one','two','three','four','five','six','seven','eight','nine']
fenweishu


# In[10]:


#投资组合数据的整合和清洗

portfolio = pd.merge(cross,fenweishu,on='month',how='left')
portfolio = portfolio.dropna(subset=['ret','next_ret'])
portfolio


# In[11]:


#按超额收益对股票进行分组标记

portfolio['sort'] = np.where(
    portfolio['ret'] <= portfolio['one'], 'P1',
    np.where(
        portfolio['ret'] <= portfolio['two'], 'P2',
        np.where(
            portfolio['ret'] <= portfolio['three'], 'P3',
            np.where(
                portfolio['ret'] <= portfolio['four'], 'P4',
                np.where(
                    portfolio['ret'] <= portfolio['five'], 'P5',
                    np.where(
                        portfolio['ret'] <= portfolio['six'], 'P6',
                        np.where(
                            portfolio['ret'] <= portfolio['seven'], 'P7',
                            np.where(
                                portfolio['ret'] <= portfolio['eight'], 'P8',
                                np.where(
                                    portfolio['ret'] <= portfolio['nine'],
                                    'P9', 'Pmax')))))))))
portfolio


# In[14]:


#计算分组后加权平均下期收益

portfolio_mom =  pd.DataFrame(
    portfolio.groupby(['month','sort']).apply(lambda x: np.average(x['next_ret'],weights = x['floatingvalue'])))
portfolio_mom


# In[15]:


#分组收益数据的格式优化
#重置索引，重命名列

portfolio_mom = portfolio_mom.reset_index()
portfolio_mom.columns = ['month', 'sort', 'p']
portfolio_mom


# In[16]:


#分组收益数据的日期调整与清洗
#日期调整
#缺失值清洗

# Month plus one month 
portfolio_mom['month'] = portfolio_mom['month'] + MonthEnd(1)
# drop NA
portfolio_mom = portfolio_mom.dropna()
portfolio_mom


# In[17]:


#因子回测的收益分析与策略构建
#重塑分组收益为宽表
#构建多空组合

portfolio_mom = portfolio_mom.pivot_table(index='month',
                                                    columns='sort',
                                                    values='p')
# long lowest return stocks and short highest return stocks
portfolio_mom['My_portfolio'] = portfolio_mom['P1'] - portfolio_mom['Pmax']
portfolio_mom


# In[18]:


#分组收益数据的时间范围筛选

portfolio_mom = portfolio_mom['1995-01':'2024-12']
portfolio_mom


# In[19]:


#多空组合收益的统计显著性检验

model_port = smf.ols('My_portfolio ~ 1',
                 data=portfolio_mom['2000-02':'2024-12']).fit(
                     cov_type='HAC', cov_kwds={'maxlags': 6})
print(model_port.summary())


# In[20]:


#市场基准收益数据的预处理

from pandas.tseries.offsets import MonthEnd # 月末
Market_ret = pd.read_csv('D:/笃行楼209/Marketret_mon_stock2024.csv')
Market_ret['month'] = pd.to_datetime(Market_ret['month'], format='%b %Y') + MonthEnd(0)
Market_ret.set_index('month', inplace=True)
Market_ret.sort_index(inplace=True)
Market_ret = Market_ret.drop(columns=['Unnamed: 0'])
Market_ret.rename(columns={'ret': 'MKT'}, inplace=True)
Market_ret


# In[22]:


#策略收益与市场基准的整合与筛选

MYPOR = portfolio_mom[['P1','Pmax','My_portfolio']]
MYPOR = MYPOR.dropna()
MYPOR = pd.merge(MYPOR,Market_ret[['MKT']],left_index=True,right_index=True,how='left')
MYPOR['month'] = pd.date_range(start = '1995',periods=len(MYPOR),freq = 'M')
MYPOR.set_index('month', inplace = True)
MYPOR = MYPOR["2000":]
MYPOR


# In[23]:


#多空组合收益的市场风险调整分析

model_port = smf.ols('My_portfolio ~ MKT',
                 data=MYPOR['2000-02':'2024-12']).fit(
                     cov_type='HAC', cov_kwds={'maxlags': 6})
print(model_port.summary())


# # 结果解读

# In[24]:


# 计算投资组合的Sharpe Ratio
sharpe_ratio = MYPOR['My_portfolio'].mean() / MYPOR['My_portfolio'].std() * np.sqrt(12)
print(f"Sharpe Ratio: {sharpe_ratio}")


# In[25]:


# 还原投资策略的价格
MYPOR_pic = MYPOR['2000-01':'2024-12'].copy()
MYPOR_pic['price_portfolio'] = (1 + MYPOR_pic['My_portfolio']).cumprod()
MYPOR_pic['price_p1'] = (1 + MYPOR_pic['P1']).cumprod()
MYPOR_pic['price_pmax'] = (1 + MYPOR_pic['Pmax']).cumprod()
MYPOR_pic['price_market'] = (1 + MYPOR_pic['MKT']).cumprod()
MYPOR_pic


# In[27]:


# 画图
fig = plt.figure(figsize=(12, 4))
plt.plot(
    'price_portfolio',  
    '.-r', 
    label='Price of My Portfolio',  
    linewidth=1, 
    data=MYPOR_pic)  
plt.title("China's Stock Market") 
plt.xlabel('Month') 
plt.ylabel('Return') 

plt.plot(
    'price_market', 
    '.-b', 
    label='Price of Market', 
    linewidth=1, 
    data=MYPOR_pic) 

plt.plot(
    'price_p1', 
    '.-g', 
    label='Price of Lowest', 
    linewidth=1, 
    data=MYPOR_pic) 

plt.plot(
    'price_pmax', 
    '.-c', 
    label='Price of Highest', 
    linewidth=1, 
    data=MYPOR_pic) 

# plot legend
plt.legend(loc='upper left')

# 添加网格线
plt.grid(True)

# 添加横线 1
plt.axhline(y=1, color='yellow', linewidth=1)

plt.show();


# In[28]:


# 计算累积收益率
MYPOR['cumulative_return'] = (1 + MYPOR['My_portfolio']).cumprod()

# 计算滚动最大值
MYPOR['rolling_max'] = MYPOR['cumulative_return'].cummax()

# 计算回撤
MYPOR['drawdown'] = MYPOR['cumulative_return'] / MYPOR['rolling_max'] - 1

# 计算最大回撤
max_drawdown = MYPOR['drawdown'].min()
print(f"Maximum Drawdown: {max_drawdown}")


# In[29]:


# 找出最大回撤的时间
max_drawdown_end = MYPOR['drawdown'].idxmin() # 返回 Series 中最小值的索引
max_drawdown_start = MYPOR.loc[:max_drawdown_end, 'cumulative_return'].idxmax() # 这部分代码选择了从数据开始到 max_drawdown_end 时间点之间的所有累积收益率

print(f"Maximum Drawdown: {max_drawdown}")
print(f"Maximum Drawdown Start Date: {max_drawdown_start}")
print(f"Maximum Drawdown End Date: {max_drawdown_end}")


# # 投资策略评估指标体系

# In[30]:


# 构建完整的策略评估函数
def calculate_strategy_metrics(returns, benchmark_returns=None, rf_rate=0.03/12):
    """
    计算投资策略的全面评估指标
    
    Parameters:
    -----------
    returns : pd.Series
        策略收益率序列（月度）
    benchmark_returns : pd.Series, optional
        基准收益率序列（用于计算Information Ratio）
    rf_rate : float
        月度无风险利率，默认年化3%
    
    Returns:
    --------
    dict : 包含所有评估指标的字典
    """
    
    metrics = {}
    
    # 1. 收益指标
    # 累积收益率
    cumulative_return = (1 + returns).prod() - 1
    metrics['累积收益率 (Cumulative Return)'] = f"{cumulative_return:.2%}"
    
    # 年化收益率
    n_months = len(returns)
    n_years = n_months / 12
    annualized_return = (1 + cumulative_return) ** (1/n_years) - 1
    metrics['年化收益率 (Annualized Return)'] = f"{annualized_return:.2%}"
    
    # 月平均收益率
    avg_monthly_return = returns.mean()
    metrics['月平均收益率 (Average Monthly Return)'] = f"{avg_monthly_return:.2%}"
    
    # 2. 风险指标
    # 收益率标准差
    volatility = returns.std()
    metrics['月度波动率 (Monthly Volatility)'] = f"{volatility:.2%}"
    
    # 年化波动率
    annualized_volatility = volatility * np.sqrt(12)
    metrics['年化波动率 (Annualized Volatility)'] = f"{annualized_volatility:.2%}"
    
    # 最大回撤
    cumulative = (1 + returns).cumprod()
    running_max = cumulative.cummax()
    drawdown = cumulative / running_max - 1
    max_drawdown = drawdown.min()
    metrics['最大回撤 (Maximum Drawdown)'] = f"{max_drawdown:.2%}"
    
    # 下行风险（只考虑负收益的标准差）
    downside_returns = returns[returns < 0]
    downside_deviation = downside_returns.std()
    metrics['下行标准差 (Downside Deviation)'] = f"{downside_deviation:.2%}"
    
    # VaR (5% 分位数)
    var_95 = returns.quantile(0.05)
    metrics['VaR (95%)'] = f"{var_95:.2%}"
    
    # CVaR (条件VaR, 最差5%的平均值)
    cvar_95 = returns[returns <= var_95].mean()
    metrics['CVaR (95%)'] = f"{cvar_95:.2%}"
    
    # 3. 风险调整收益指标
    # 夏普比率
    excess_returns = returns - rf_rate
    sharpe_ratio = excess_returns.mean() / excess_returns.std() * np.sqrt(12)
    metrics['夏普比率 (Sharpe Ratio)'] = f"{sharpe_ratio:.4f}"
    
    # 索提诺比率（使用下行风险）
    if downside_deviation > 0:
        sortino_ratio = excess_returns.mean() / downside_deviation * np.sqrt(12)
        metrics['索提诺比率 (Sortino Ratio)'] = f"{sortino_ratio:.4f}"
    else:
        metrics['索提诺比率 (Sortino Ratio)'] = "N/A"
    
    # 卡玛比率（年化收益率/最大回撤）
    if max_drawdown < 0:
        calmar_ratio = annualized_return / abs(max_drawdown)
        metrics['卡玛比率 (Calmar Ratio)'] = f"{calmar_ratio:.4f}"
    else:
        metrics['卡玛比率 (Calmar Ratio)'] = "N/A"
    
    # 信息比率（如果提供了基准）
    if benchmark_returns is not None:
        active_returns = returns - benchmark_returns
        tracking_error = active_returns.std()
        if tracking_error > 0:
            information_ratio = active_returns.mean() / tracking_error * np.sqrt(12)
            metrics['信息比率 (Information Ratio)'] = f"{information_ratio:.4f}"
            metrics['跟踪误差 (Tracking Error)'] = f"{tracking_error * np.sqrt(12):.2%}"
        else:
            metrics['信息比率 (Information Ratio)'] = "N/A"
            metrics['跟踪误差 (Tracking Error)'] = "N/A"
    
    # 4. 其他重要指标
    # 胜率
    win_rate = (returns > 0).sum() / len(returns)
    metrics['胜率 (Win Rate)'] = f"{win_rate:.2%}"
    
    # 盈亏比
    avg_gain = returns[returns > 0].mean()
    avg_loss = abs(returns[returns < 0].mean())
    if avg_loss > 0:
        profit_loss_ratio = avg_gain / avg_loss
        metrics['盈亏比 (Profit/Loss Ratio)'] = f"{profit_loss_ratio:.4f}"
    else:
        metrics['盈亏比 (Profit/Loss Ratio)'] = "N/A"
    
    # 最长回撤期
    # 找到所有创新高的时间点
    is_new_high = cumulative == running_max
    new_high_dates = cumulative[is_new_high].index
    
    # 计算每次创新高之间的间隔
    if len(new_high_dates) > 1:
        max_drawdown_duration = 0
        for i in range(len(new_high_dates) - 1):
            duration = (new_high_dates[i+1] - new_high_dates[i]).days / 30  # 转换为月
            max_drawdown_duration = max(max_drawdown_duration, duration)
        metrics['最长回撤期 (月) (Max Drawdown Duration)'] = f"{max_drawdown_duration:.1f}"
    else:
        metrics['最长回撤期 (月) (Max Drawdown Duration)'] = "N/A"
    
    # 最大回撤起止时间
    max_dd_end = drawdown.idxmin()
    max_dd_start = cumulative.loc[:max_dd_end].idxmax()
    metrics['最大回撤开始时间'] = max_dd_start.strftime('%Y-%m')
    metrics['最大回撤结束时间'] = max_dd_end.strftime('%Y-%m')
    
    return metrics

# 使用示例
print("=" * 80)
print("投资组合策略评估指标")
print("=" * 80)
portfolio_metrics = calculate_strategy_metrics(
    MYPOR['My_portfolio'], 
    benchmark_returns=MYPOR['MKT'],
    rf_rate=0.03/12  # 假设年化无风险利率为3%
)

for key, value in portfolio_metrics.items():
    print(f"{key:.<50} {value:>20}")
print("=" * 80)


# In[31]:


# 对比分析：我的策略 vs 市场 vs P1 vs Pmax
print("\n" + "=" * 80)
print("不同投资组合的对比分析")
print("=" * 80)

strategies = {
    '反转策略 (My Portfolio)': MYPOR['My_portfolio'],
    '市场组合 (Market)': MYPOR['MKT'],
    '最低收益组 (P1)': MYPOR['P1'],
    '最高收益组 (Pmax)': MYPOR['Pmax']
}

comparison_results = []

for name, returns in strategies.items():
    print(f"\n【{name}】")
    print("-" * 80)
    metrics = calculate_strategy_metrics(returns, benchmark_returns=MYPOR['MKT'], rf_rate=0.03/12)
    
    # 只显示关键指标
    key_metrics = [
        '累积收益率 (Cumulative Return)',
        '年化收益率 (Annualized Return)',
        '年化波动率 (Annualized Volatility)',
        '最大回撤 (Maximum Drawdown)',
        '夏普比率 (Sharpe Ratio)',
        '索提诺比率 (Sortino Ratio)',
        '卡玛比率 (Calmar Ratio)',
        '胜率 (Win Rate)'
    ]
    
    for key in key_metrics:
        if key in metrics:
            print(f"{key:.<50} {metrics[key]:>20}")
    
    # 保存用于后续对比
    comparison_results.append({
        '策略': name,
        '年化收益': metrics['年化收益率 (Annualized Return)'],
        '年化波动': metrics['年化波动率 (Annualized Volatility)'],
        '夏普比率': metrics['夏普比率 (Sharpe Ratio)'],
        '最大回撤': metrics['最大回撤 (Maximum Drawdown)']
    })

print("\n" + "=" * 80)


# In[32]:


# 创建对比表格
comparison_df = pd.DataFrame(comparison_results)
print("\n策略对比汇总表:")
print(comparison_df.to_string(index=False))
print("\n")


# ## 回撤分析
# 
# 详细分析策略的回撤情况

# In[33]:


# 绘制完整的回撤图
cumulative_return = (1 + MYPOR['My_portfolio']).cumprod()
running_max = cumulative_return.cummax()
drawdown = (cumulative_return / running_max - 1) * 100  # 转换为百分比

fig, axes = plt.subplots(2, 1, figsize=(10, 6))

# 累积收益率和最大值
axes[0].plot(cumulative_return.index, cumulative_return, 'b-', label='Strategy Net Value', linewidth=2)
axes[0].plot(running_max.index, running_max, 'r--', label='Historical Peak Value', linewidth=1, alpha=0.7)
axes[0].set_title('Strategy Net Value Curve', fontsize=14, fontweight='bold')
axes[0].set_ylabel('Cumulative Net Value')
axes[0].legend(loc='upper left')
axes[0].grid(True, alpha=0.3)

# 回撤曲线
axes[1].fill_between(drawdown.index, drawdown, 0, color='red', alpha=0.3)
axes[1].plot(drawdown.index, drawdown, 'r-', linewidth=1)
axes[1].set_title('Drawdown Curve', fontsize=14, fontweight='bold')
axes[1].set_ylabel('Drawdown (%)')
axes[1].set_xlabel('Time')
axes[1].grid(True, alpha=0.3)

# 标注最大回撤点
max_dd_value = drawdown.min()
max_dd_date = drawdown.idxmin()
axes[1].plot(max_dd_date, max_dd_value, 'ro', markersize=10)
axes[1].annotate(f'Max Drawdown: {max_dd_value:.2f}%\n{max_dd_date.strftime("%Y-%m")}',
                xy=(max_dd_date, max_dd_value),
                xytext=(20, 20), textcoords='offset points',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.7),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.tight_layout();
plt.show();


# ## 年度和季度表现分析
# 
# 分析策略在不同时间段的表现

# In[34]:


# 年度表现
annual_returns = MYPOR.groupby(MYPOR.index.year).agg({
    'My_portfolio': lambda x: (1 + x).prod() - 1,
    'MKT': lambda x: (1 + x).prod() - 1,
    'P1': lambda x: (1 + x).prod() - 1,
    'Pmax': lambda x: (1 + x).prod() - 1
})

annual_returns.columns = ['Reversal Strategy', 'Market', 'Lowest Return Group', 'Highest Return Group']
annual_returns = annual_returns * 100  # Convert to percentage

print("Annual Returns (%):")
print(annual_returns.round(2))
print(f"\nAverage Annual Return (%): {annual_returns['Reversal Strategy'].mean():.2f}")
print(f"Annual Return Std Dev (%): {annual_returns['Reversal Strategy'].std():.2f}")
print(f"Positive Return Years: {(annual_returns['Reversal Strategy'] > 0).sum()}/{len(annual_returns)}")

# Plot annual returns comparison
fig, ax = plt.subplots(figsize=(10, 6))
x = np.arange(len(annual_returns))
width = 0.2

bars1 = ax.bar(x - 1.5*width, annual_returns['Reversal Strategy'], width, label='Reversal Strategy', alpha=0.8)
bars2 = ax.bar(x - 0.5*width, annual_returns['Market'], width, label='Market', alpha=0.8)
bars3 = ax.bar(x + 0.5*width, annual_returns['Lowest Return Group'], width, label='Lowest Return Group', alpha=0.8)
bars4 = ax.bar(x + 1.5*width, annual_returns['Highest Return Group'], width, label='Highest Return Group', alpha=0.8)

ax.set_xlabel('Year')
ax.set_ylabel('Return (%)')
ax.set_title('Annual Returns Comparison', fontsize=14, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(annual_returns.index, rotation=45)
ax.legend()
ax.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
ax.grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.show();


# In[35]:


# 季度表现热力图
MYPOR_copy = MYPOR.copy()
MYPOR_copy['Year'] = MYPOR_copy.index.year
MYPOR_copy['Quarter'] = MYPOR_copy.index.quarter

quarterly_returns = MYPOR_copy.groupby(['Year', 'Quarter'])['My_portfolio'].apply(
    lambda x: (1 + x).prod() - 1
) * 100

# 转换为透视表
quarterly_pivot = quarterly_returns.reset_index().pivot(index='Year', columns='Quarter', values='My_portfolio')
quarterly_pivot.columns = ['Q1', 'Q2', 'Q3', 'Q4']

# 添加年度收益率列
quarterly_pivot['Annual'] = annual_returns['Reversal Strategy']

print("\nQuarterly Returns (%):")
print(quarterly_pivot.round(2))

# 绘制热力图
fig, ax = plt.subplots(figsize=(10, 12))
im = ax.imshow(quarterly_pivot.values, cmap='RdYlGn', aspect='auto', vmin=-20, vmax=20)

# 设置坐标轴
ax.set_xticks(np.arange(len(quarterly_pivot.columns)))
ax.set_yticks(np.arange(len(quarterly_pivot.index)))
ax.set_xticklabels(quarterly_pivot.columns)
ax.set_yticklabels(quarterly_pivot.index)

# 在每个格子中显示数值
for i in range(len(quarterly_pivot.index)):
    for j in range(len(quarterly_pivot.columns)):
        value = quarterly_pivot.values[i, j]
        if not np.isnan(value):
            text = ax.text(j, i, f'{value:.1f}%',
                         ha="center", va="center", color="black", fontsize=9)

ax.set_title('Quarterly Returns Heatmap', fontsize=14, fontweight='bold', pad=20)
ax.set_xlabel('Quarter')
ax.set_ylabel('Year')

# 添加颜色条
cbar = plt.colorbar(im, ax=ax)
cbar.set_label('Return (%)', rotation=270, labelpad=20)

plt.tight_layout()
plt.show();


# In[ ]:




